﻿//C++11 重大变化 -- 新增容器和新增接口
// lambda 表达式
// 包装器
// 


#include <iostream>
#include <string>
using namespace std;


// 默认的移动构造和移动赋值
// 1 . 条件 
//		若没有实现移动构造 且 **** 没有实现析构/拷贝/赋值任意一个 , 那么编译器会自动生成 ****
//      默认的移动构造

// 2.  条件
//		若没有实现拷贝赋值 且 **** 没有实现析构/拷贝/赋值任意一个 , 那么编译器会自动生成 ****
//		默认的移动赋值


// 默认生成的移动构造和拷贝赋值
// 1 . 对内置类型完成浅拷贝(一个字节一个字节拷贝)
// 2 . 对自定义类型 , 若自定义类型实现了移动构造 - 就调用它的移动构造, 若没有实现就调用拷贝构造
//     对自定义类型 , 若自定义类型实现了移动赋值 - 就调用它的移动赋值, 若没有实现就调用拷贝赋值
// 


//default 强制
//class Person
//{
//	
//public :
//
//	//构造
//	Person(const char* name, int age)
//		: _name(name)
//		, _age(age)
//	{}
//
//	//拷贝构造
//	//Person(const Person& p)
//	//	:_name(p._name)
//	//	 ,_age(p._age)
//	//{}
//
//	//强制默认构造
//	Person() = default;
//	
//	//限制生成默认拷贝构造
//	Person(const Person& p) = delete;
//
//
//private:
//	string _name;
//	int _age;
//};
//
//
//
//int main()
//{
//	Person s1;
//	Person s2 = s1;
//	Person s3 = std::move(s1);
//
//	return 0;
//}


//////////////////////////////// lambda

// 1. lambda **** 是一个匿名函数对象 **** , 可以定义在内部

// 写法 :
// []      ()        ->return type {    }
//  |       |             |           |
//捕捉列表  参数          返回值类型        函数体


// **** 捕捉列表 和 函数体不能省略



//全局变量
int num1 = 100;


//lambda  基本用法
void Test01()
{
	// lambda -- 是一个匿名函数对象 , 所以用 auto 自动推导类型 
	auto add = [](int a, int b)->int {return a + b; };

	cout << add(1, 2) << endl;
	cout << add(2, 3) << endl;
	cout << add(4, 5) << endl;


	// 可以省略 --- (参数部分) , 返回值类型 ; 返回值类型通常不写 , 由编译器自动推导
	auto print = [] {cout << "hello world !" << endl; };

	print();

	//写一个 hello C++ 的 lambda 
	auto f1 = [] {
		cout << "hello C++" << endl;
		};

	f1();

	// 写一个交换两个变量的函数 


	auto swap = [](int& a, int& b){
		int tmp = 0;
		tmp = a;
		a = b;
		b = tmp;
	};
	int a = 1, b = 2;
	cout << a << b << endl;
	swap(a , b);
	cout << a << b << endl;
	
}


// lambda 捕捉列表
// 捕捉可以捕捉值 和 引用


// lambda 可以定义在全局 ,但一般不这么用
//全局的话什么都不用捕捉 , 所以捕捉列表必须是空的

// 全局的 lambda 
auto f0 = [] {};


void Test02()
{
	int a = 1, b = 2;

	//只能用当前 f 函数所在作用域的变量 , 需要用外面的需要捕捉
	auto f = [](){
		//a++; err
		int c = 0;
		c++; // ok
};

	//捕捉值 , f1 内部不影响外部 , 捕捉列表是被 const 修饰的 , 传值不能被修改
	auto f1 = [a , b]() {
		//++a;//err
		//++b;//err
		cout << a << b << endl; // ok

		};

	//捕捉引用  ***** 特别注意这里是 & , **不是取地址**
		auto f2 = [&a, &b]() {
		++a;//err
		++b;//err
		cout << a << b << endl; // ok

		};

		f2();


		// 2.  隐式捕捉
		// 隐式值捕捉 , 隐式引用捕捉

		int n1 = 1, n2 = 2, n3 = 3, n4 = 4;

		//隐式值捕捉 -- 用哪些值捕捉哪些值
		// = 
		auto f3 = [=]{
			//隐式捕捉值
			cout << n1 << n2 << endl;
			int ret = n1 + n2;
			cout << ret << endl;
		};

		f3();

		//隐式引用捕捉 -- 用哪些值捕捉哪些值
		// &
		auto f4 = [&] {
			//隐式引用捕捉
			++a; 
			++b;
			++n2;
		};
		f4();

		cout << a << b << n2 << endl;

		// 3. 混合捕捉
		// 混合捕捉 [] 中第一个必须是 = / &
		// 后面的参数必须与第一个参数不同
		// 若第一个参数是 & , 后面的必须是值捕捉

		//混合捕捉1 
		auto f5 = [&, a , b] {
			//混合捕捉
			cout << a << b << n1 << endl;
			//++b;
			++n1;
			++n2;
			};
		f5();


		//// 混合捕捉2 
		//一部分值捕捉 , 一部分引用捕捉
		auto f6 = [= , &n1 , &n2] {
			//混合捕捉
			cout << a << b << n1 << endl;
			//++b;
			++n1;
			++n2;
			};
		f6();


		///全局和局部静态变量不能捕捉
		// 可以直接使用

		static int m = 10;
		/*//err
		auto f7 = [m] {
			};
		f7();
*/

		//局部静态可以直接使用
		auto f7 = [] {

			cout << m << endl; // ok

			};
		f7();


		//全局变量也可以直接使用 ,因为全局对整个工程都有用
		auto f8 = [] {

			// num1 为全局变量
			cout << num1 << endl; // ok

			};
		f8();
}

// lambda 应用
struct Goods
{
	string _name; // 名字
	double _price; // 价格
	int _evaluate; // 评价
	// ...
	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate)
	{}

};

struct ComparePriceLess
{
	bool operator()(const Goods& gl, const Goods& gr)
	{
		return gl._price < gr._price;
	}
};

struct ComparePriceGreater
{
	bool operator()(const Goods& gl, const Goods& gr)
	{
		return gl._price > gr._price;
	}
};
#include <vector>
#include <algorithm>
void Test03()
{
	vector<Goods> v = { { "苹果", 2.1, 5 }, { "⾹蕉", 3, 4 },
						{ "橙⼦", 2.2, 3}, { "菠萝", 1.5, 4 } };

	//没有 lambda 比较价格等都要我们在外面写一个仿函数 , 然后传仿函数对象来比较
	sort(v.begin(), v.end(), ComparePriceLess()); // 这里要传对象 ,因为是函数
	sort(v.begin(),v.end(), ComparePriceGreater()); // 这里要传对象 ,因为是函数
	//以上这样写很麻烦 , 还要定义一个仿函数在调用

	//// 用 lambda ,  lambda 是匿名函数对象 , 底层就是仿函数的匿名对象

	//用 lambda , price 降序
	sort(v.begin(), v.end(), [](const Goods& goods1, const Goods& goods2) {
									return goods1._price > goods2._price; });
	//用 lambda , price 升序
	sort(v.begin(), v.end(), [](const Goods& goods1, const Goods& goods2) {
		return goods1._price < goods2._price; });

	//用 lambda , evaluate 升序
	sort(v.begin(), v.end(), [](const Goods& goods1, const Goods& goods2) {
		return goods1._evaluate < goods2._evaluate; });

	//用 lambda , evaluate 降序
	sort(v.begin(), v.end(), [](const Goods& goods1, const Goods& goods2) {
		return goods1._evaluate > goods2._evaluate; });


	// lambda 就很方便
}



//lambda 原理
//lambda 底层就是仿函数对象 , 编译器会根据 lambda 生成对应的仿函数 
// []捕捉列表就是仿函数类的成员变量
// () 参数就是仿函数 operator() 的参数
// 返回值类型和函数体就是仿函数的返回值类型和函数体

//只不过写了 lambda 我们不用写仿函数了 , 只是编译器帮我们生成了


class Rate
{
	
public :
	Rate(double rate)
		: _rate(rate)
	{
	}
	double operator()(double money, int year)
	{
		return money * _rate * year;
	}
private:
	double _rate;
};


void Test04()
{
	double rate = 0.49;

	// lambda
	auto r2 = [rate](double money, int year) {
		return money * rate * year;
		};

	// 函数对象
	Rate r1(rate);
	r1(10000, 2);
	r2(10000, 2);

	auto func1 = [] {
		cout << "hello world" << endl;
		};
	func1();
}




//////////////////////////////// 包装器

// function --- 可以包装一切可调用对象
// 可以包装 : 函数指针 , 仿函数 , lambda , bind ..... 
//可以统一类型

//template <class Ret, class... Args>
//class function<Ret(Args...)>;
//          function<int(int , int)>    



//包装器
//需要头文件 -- #include<functional>
//#include <functional>
//
////普通函数
//int f(int a, int b)
//{
//	return a + b;
//}
//
////仿函数
//struct Functor
//{ 
//public :
//	int operator() (int a, int b)
//	{
//		return a + b;
//	}
//};

//类
//class Plus
//{
//
//public:
//	Plus(int n = 10)
//		: _n(n)
//	{
//	}
//
//	//静态成员函数
//	static int plusi(int a, int b)
//	{
//		return a + b;
//	}
//
//	double plusd(double a, double b)
//	{
//		return (a + b) * _n;
//	}
//private:
//	int _n;
//};

//void Test05()
//{
//
//	//function 包装一切可调用对象
//
//	//// 1 . 包装普通
//	
//	//包装函数指针
//	function<int(int, int)> f1 = f;
//	//仿函数对象
//	function<int(int, int)> f2 = Functor();
//	//lambda 
//	function<int(int , int)> f3 = [](int a, int b) { return a + b; };
//
//	//对以上统一了类型
//	cout << f1(1, 1) << endl;
//	cout << f2(2, 2) << endl;
//	cout << f3(3, 3) << endl;
//
//
//	//// 2 . 包装成员函数 -- 要指定类域 + 地址
//	// 1. 包装静态成员函数
//
//	//静态成员函数地址可加可不加 , 但这里统一加&
//	function<int(int, int)> f4 = &Plus::plusi;
//
//	//普通成员函数 , 因为包装器包装要类型和参数匹配
//	// 普通成员函数有隐含的 this 指针 ,所以传指针和对象
//
//	//1 . 传指针
//	function<double(Plus*, double, double)> f5 = &Plus::plusd;
//	Plus p;
//
//	cout << f4(1, 1) << endl;
//	cout << f5(&p , 2, 2) << endl;
//
//	//2 . 传对象
//	function<double(Plus, double, double)> f6 = &Plus::plusd;
//	cout << f6(p, 2, 2) << endl;
//
//	//3 . 传引用
//	function<double(Plus&&, double, double)> f7 = &Plus::plusd;
//	cout << f7(move(p), 2, 2) << endl;
//	//传匿名对象
//	cout << f7(Plus(100), 2, 2) << endl;
//
//
//}


//bind -- 绑定
// bind 可以用来调整参数个数(最最最实用) 和 参数顺序
#include <functional>

//占位符放在 placeholders 

//这就是占位符 , 1 对应实参的第一个 , 2 对应实参的第二个 ....
using placeholders::_1;
using placeholders::_2;

int Sub(int a, int b)
{
	return (a - b) * 10;
} 
int SubX(int a, int b, int c)
{
	return (a - b - c) * 10;
} 
class Plus
{ 
public :
	static int plusi(int a, int b)
	{
		return a + b;
	} 
	double plusd(double a, double b)
	{
		return a + b;
	}
};

void Test06()
{
	// bind 调整参数顺序, 基本不用
	auto ret = bind(Sub, _1, _2);
	// 10 对应 _1 , 20 对应 _2
	cout << ret(10, 20) << endl;

	//调整顺序
	auto ret1 = bind(Sub, _2, _1);
	// 10 对应 _1 , 20 对应 _2
	cout << ret1(10, 20) << endl;

	////////////////// 真正的应用

	//调整参数个数
	// 即 : 绑定一个值 ,我们就不需要传了
	auto ret2 = bind(Sub, 100, _1);
	// _1 对于 20 , 100就被绑定了 , 一直是 100
	cout << ret2(20) << endl;

	// Sub , 20 被绑定了
	auto ret3 = bind(Sub, _1, 20);
	cout << ret3(100) << endl;

	//绑定第一个参数
	auto ret4 = bind(SubX, 100, _1, _2);
	// 20 -- _1 , 40 -- _2
	cout << ret4(20, 40) << endl;

	//绑定第2个参数
	auto ret5 = bind(SubX, _1, 100 , _2);
	// 20 -- _1 , 40 -- _2
	cout << ret5(20, 40) << endl;

	//绑定第3个参数
	auto ret6 = bind(SubX, _1,_2,100);
	// 20 -- _1 , 40 -- _2
	cout << ret6(20, 40) << endl;

	//绑定2个参数
	auto ret7 = bind(SubX, _1, 100,1000);
	// 20 -- _1 , 40 -- _2
	cout << ret7(20) << endl;


	//////////////// 实践中很有用的一个 ////////////////
	// 对于包装成员函数 ,需要传 this 指针, 这里直接包装就不要传了

	//绑死成员对象 , 即不用传 this了
	function<double(double,double)> ff = bind(&Plus::plusd, Plus(), _1, _2);
	cout << ff(10.2, 11.1) << endl;
}


int main()
{
	//lambda  基本用法
	//Test01();

	//捕捉列表
	//Test02();

	//lambda 
	//Test03();

	//lambda 原理
	//Test04();

	//包装器 function
	//Test05();

	//bind
	Test06();
	return 0;
}